package com.zte.claa.inficombo.csa.app.service.nbiot.impl;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.script.Invocable;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.zte.claa.inficombo.csa.app.config.CmccOnenetApiPara;
import com.zte.claa.inficombo.csa.app.dao.mysql.impl.DevTypeMapper;
import com.zte.claa.inficombo.csa.app.dao.mysql.impl.ProjCustAttrMapper;
import com.zte.claa.inficombo.csa.app.dao.mysql.impl.SysParamMapper;
import com.zte.claa.inficombo.csa.app.model.dev.DevBase;
import com.zte.claa.inficombo.csa.app.model.dev.DevTpExtData;
import com.zte.claa.inficombo.csa.app.model.proj.ProjCustAttrBean;
import com.zte.claa.inficombo.csa.app.model.proj.SysParameter;
import com.zte.claa.inficombo.csa.app.model.redis.ProjectDev;
import com.zte.claa.inficombo.csa.app.service.dev.DevBaseService;
import com.zte.claa.inficombo.csa.app.service.jsplugin.JSParam;
import com.zte.claa.inficombo.csa.app.service.nbiot.CmccNBCmdAck;
import com.zte.claa.inficombo.csa.app.service.nbiot.CmccNBCmdReq;
import com.zte.claa.inficombo.csa.app.service.nbiot.NBIotCmdAck;
import com.zte.claa.inficombo.csa.app.service.rptparse.ParserContainer;
import com.zte.claa.inficombo.csa.app.util.HttpClientUtils;
import com.zte.claa.inficombo.csa.app.util.JsonUtils;

/**
 * 中移物联网平台OneNET命令下发服务接口
 * 
 * @author 0283000115
 */

@Service
public class NBIotCmccCmdExecService {
	// 中移动ONENet默认域名.
	private static final String CMCC_ONENET_SVRURL = "http://api.heclouds.com";
	
	// 系统参数配置.
	private static final String SYSPARA_CMCC_API_CATEGORY = "CMCC.ONENET.API.PARAS";
	private static final String SYSPARA_CMCC_API_SVRDOMAIN = "SVRDOMAIN";
	private static final String SYSPARA_CMCC_API_APIKEY = "APIKEY";
	
	// CMCC-OneNET平台ApiKEY项目定制化参数（优先查找该配置，没有的话再从系统参数表中查找默认的统一配置）
	private static final String PROJ_CUST_CMCC_NB_APIKEY = "CMCC.OneNET.NB.MasterAPIKey";
	private static final String PROJ_CUST_CMCC_GPRS_APIKEY = "CMCC.OneNET.GRPS.MasterAPIKey";

	// NB设备fntype
	public static final int NB_DEV_FNTYPE = 4096;
	public static final int NB_DEV_TCPT_TRANSFER_FNTYPE = 4097;
	
	// 日志记录
	private final Logger logger = LoggerFactory.getLogger(NBIotCmccCmdExecService.class);		
	
	//@Autowired
	//private BaseCacheManager cacheManager;
	
	@Autowired
	private ParserContainer container;
	
	@Autowired
	private CmccOnenetApiPara cmccApiPara;
	
	@Autowired
	private DevBaseService devService;
	
	@Autowired
	private SysParamMapper sysParaService;
	
	@Autowired
	private DevTypeMapper devtpMapper;
	
	@Autowired
	private ProjCustAttrMapper projAttrService;

	// 针对CMCC-NBIOT设备下发命令
	public NBIotCmdAck execCmccNBIotCmd(DevBase baseDev, String payload, Map<String, Object> extParaMap, boolean offlineMode) throws Exception {
		// 构造请求.
		String devEui = baseDev.getDevEui();
		ProjectDev dev = this.devService.queryProjectDevByEui(devEui);
		String imei = dev.getAppkey();	
		// 构造命令请求.
		CmccNBCmdReq cmd = new CmccNBCmdReq();
		cmd.setImei(imei);
		cmd.setObj_id(this.cmccApiPara.getObjectid());
		cmd.setObj_inst_id(this.cmccApiPara.getObjectinstid());
		cmd.setRes_id(this.cmccApiPara.getResid());
		cmd.setRetry(3);
		// js插件组织args
		cmd.setArgs(this.buildNbIotCmdArgs(dev, payload, extParaMap));		
		// 判断是否为CMCC-NB TCP透传设备.
		if(this.isCmccTcpTransferDev(baseDev)){
			return this.execCmccOneNetTcpTranferCmd(cmd, baseDev);
		}
		// 执行NB终端命令.
		CmccNBCmdAck ack = null;
		if(offlineMode){
			ack = this.execCmccOneNetOfflineCmd(baseDev.getProjectId(), cmd);
		} else {
			ack = this.execCmccOneNetCmd(baseDev.getProjectId(), cmd);
		}
		return ack;
	}
	
	// 执行Cmcc-OneNet即时命令 （write模式）
	public CmccNBCmdAck execCmccOneNetCmdInWriteMode(String projectId, CmccNBCmdReq cmd) throws Exception {
		// URL: http://api.heclouds.com/nbiot
		String cmccNbIotSvrUrl = this.getCmccOneNetDomain();
		String cmccApiKey = this.getCmccOneNetApiKey(PROJ_CUST_CMCC_NB_APIKEY, projectId);    
		StringBuffer urlBuf = new StringBuffer(cmccNbIotSvrUrl);
		urlBuf.append("/nbiot"); 
		urlBuf.append("?imei=").append(cmd.getImei());
		urlBuf.append("&obj_id=").append(cmd.getObj_id());
		urlBuf.append("&obj_inst_id=").append(cmd.getObj_inst_id());
		// mode=1: replace, =2:update;
		urlBuf.append("&mode=1");
		int timeOut = cmd.getTimeout();
		timeOut = (timeOut < 5) ? 5 : timeOut;
		timeOut = (timeOut > 40) ? 40 : timeOut;
		urlBuf.append("&timeout=").append(timeOut);
		
		// masterApiKey, netnet api要求放到header中.
		Map<String, String> headerParaMap = new HashMap<String, String>();
		headerParaMap.put("api-key", cmccApiKey);

		// cmcc-nbiot-data: [ {"res_id": 1, "type":1, "val": hexstr } ];
		Map<String, Object> dataItemMap = new HashMap<String, Object>();
		dataItemMap.put("res_id", cmd.getRes_id());
		//dataItemMap.put("type", 1);
		//byte[] argsBytes = cmd.getArgs().getBytes("UTF-8");
		//dataItemMap.put("val", SecurityUtil.parseByte2HexStr(argsBytes));
		dataItemMap.put("val", cmd.getArgs());
		List<Map<String, Object>> dataList = new ArrayList<Map<String, Object>>();
		dataList.add(dataItemMap);
		
		// post-req-para
		JSONObject argsJson = new JSONObject();
		argsJson.put("data", dataList);
		CmccNBCmdAck ack = new CmccNBCmdAck();
		logger.info("execCmccOneNetCmd cmd: {}, url={}, data={}", cmd.toString(), urlBuf.toString(), argsJson.toString());
		try{
			String rsp = HttpClientUtils.sendHttpPostJsonWithHeadPara(urlBuf.toString(), argsJson.toString(), headerParaMap, null);
			logger.info("execCmccOneNetCmd rsp: "+rsp);
			JSONObject ackJson = new JSONObject(rsp);
			String errNoStr = JsonUtils.getJsonAttrVal(ackJson, "errno", "0");
			String error = JsonUtils.getJsonAttrVal(ackJson, "error", "");
			ack.setErrno(Integer.parseInt(errNoStr));
			ack.setError(error);
		}catch(Exception e){
			logger.error("execCmccOneNetCmd failed: {}", cmd.toString(), e);
			// 非onenet的错误码.
			ack.setErrno(2); 
			ack.setError(e.getMessage());
		}
		return ack;
	}
		
	// 执行Cmcc-OneNet离线（缓存）命令(write模式）.
	public CmccNBCmdAck execCmccOneNetOfflineCmdInWriteMode(String projectId, CmccNBCmdReq cmd) throws Exception {
		// POST URL: http://api.heclouds.com/nbiot/offline
		String cmccNbIotSvrUrl = this.getCmccOneNetDomain();
		String cmccApiKey = this.getCmccOneNetApiKey(PROJ_CUST_CMCC_NB_APIKEY, projectId);    
		StringBuffer urlBuf = new StringBuffer(cmccNbIotSvrUrl);
		urlBuf.append("/nbiot/offline"); 
		urlBuf.append("?imei=").append(cmd.getImei());
		urlBuf.append("&obj_id=").append(cmd.getObj_id());
		urlBuf.append("&obj_inst_id=").append(cmd.getObj_inst_id());
		urlBuf.append("&mode=1");
		int timeOut = cmd.getTimeout();
		timeOut = (timeOut < 5) ? 5 : timeOut;
		timeOut = (timeOut > 40) ? 40 : timeOut;
		urlBuf.append("&timeout=").append(timeOut);
		
		String valid_time = cmd.getValid_time();
		if(valid_time != null && !valid_time.trim().equals("")){
			urlBuf.append("&valid_time=").append(valid_time);
		}
		String expired_time = cmd.getExpired_time();
		if(expired_time == null || expired_time.trim().equals("")){
			SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
			// 默认有效期为24小时；
			expired_time = formater.format(new Date(System.currentTimeMillis() + 24*3600*1000L));
		}
		urlBuf.append("&expired_time=").append(expired_time);
		urlBuf.append("&retry=").append(cmd.getRetry());
		urlBuf.append("&trigger_msg=").append(cmd.getTrigger_msg());
		
		// masterApiKey, netnet api要求放到header中.
		Map<String, String> headerParaMap = new HashMap<String, String>();
		headerParaMap.put("api-key", cmccApiKey);
		
		// cmcc-nbiot-data: [ {"res_id": 1, "type":1, "val": hexstr } ];
		Map<String, Object> dataItemMap = new HashMap<String, Object>();
		dataItemMap.put("res_id", cmd.getRes_id());
		// dataItemMap.put("type", 1);
		// byte[] argsBytes = cmd.getArgs().getBytes("UTF-8");
		// dataItemMap.put("val", SecurityUtil.parseByte2HexStr(argsBytes));
		dataItemMap.put("val", cmd.getArgs()); 
		List<Map<String, Object>> dataList = new ArrayList<Map<String, Object>>();
		dataList.add(dataItemMap);
		
		// post-req-para
		JSONObject argsJson = new JSONObject();
		argsJson.put("data", dataList);
				
		CmccNBCmdAck ack = new CmccNBCmdAck();
		logger.info("execCmccOneNetOfflineCmd cmd: {}, url={}, data={}", cmd.toString(), urlBuf.toString(), argsJson.toString());
		try{
			String rsp = HttpClientUtils.sendHttpPostJsonWithHeadPara(urlBuf.toString(), argsJson.toString(), headerParaMap, null);
			logger.info("execCmccOneNetOfflineCmd rsp: "+rsp);
			JSONObject ackJson = new JSONObject(rsp);
			String errNoStr = JsonUtils.getJsonAttrVal(ackJson, "errno", "0");
			String error = JsonUtils.getJsonAttrVal(ackJson, "error", "");
			ack.setErrno(Integer.parseInt(errNoStr));
			ack.setError(error);
		}catch(Exception e){
			logger.error("execCmccOneNetOfflineCmd failed: {}", cmd.toString(), e);
			// 非onenet的错误码.
			ack.setErrno(2); 
			ack.setError(e.getMessage());
		}
		return ack;
	}
	
	// 执行Cmcc-OneNet即时命令. 
	public CmccNBCmdAck execCmccOneNetCmd(String projectId, CmccNBCmdReq cmd) throws Exception {
		// URL: http://api.heclouds.com/nbiot/execute
		String cmccNbIotSvrUrl = this.getCmccOneNetDomain();
		String cmccApiKey = this.getCmccOneNetApiKey(PROJ_CUST_CMCC_NB_APIKEY, projectId);    
		StringBuffer urlBuf = new StringBuffer(cmccNbIotSvrUrl);
		urlBuf.append("/nbiot/execute"); 
		urlBuf.append("?imei=").append(cmd.getImei());
		urlBuf.append("&obj_id=").append(cmd.getObj_id());
		urlBuf.append("&obj_inst_id=").append(cmd.getObj_inst_id());
		urlBuf.append("&res_id=").append(cmd.getRes_id());
		int timeOut = cmd.getTimeout();
		timeOut = (timeOut < 5) ? 5 : timeOut;
		timeOut = (timeOut > 40) ? 40 : timeOut;
		urlBuf.append("&timeout=").append(timeOut);
		
		// masterApiKey, netnet api要求放到header中.
		Map<String, String> headerParaMap = new HashMap<String, String>();
		headerParaMap.put("api-key", cmccApiKey);
		
		// post-req-para
		JSONObject argsJson = new JSONObject();
		argsJson.put("args", cmd.getArgs());
		CmccNBCmdAck ack = new CmccNBCmdAck();
		logger.info("execCmccOneNetCmd cmd: {}, url={}, args={}", cmd.toString(), urlBuf.toString(), argsJson.toString());
		try{
			String rsp = HttpClientUtils.sendHttpPostJsonWithHeadPara(urlBuf.toString(), argsJson.toString(), headerParaMap, null);
			logger.info("execCmccOneNetCmd rsp: "+rsp);
			JSONObject ackJson = new JSONObject(rsp);
			String errNoStr = JsonUtils.getJsonAttrVal(ackJson, "errno", "0");
			String error = JsonUtils.getJsonAttrVal(ackJson, "error", "");
			ack.setErrno(Integer.parseInt(errNoStr));
			ack.setError(error);
		}catch(Exception e){
			logger.error("execCmccOneNetCmd failed: {}", cmd.toString(), e);
			// 非onenet的错误码.
			ack.setErrno(2); 
			ack.setError(e.getMessage());
		}
		return ack;
	}
	
	// 执行Cmcc-OneNet离线（缓存）命令.
	public CmccNBCmdAck execCmccOneNetOfflineCmd(String projectId, CmccNBCmdReq cmd) throws Exception {
		// POST URL: http://api.heclouds.com/nbiot/execute/offline
		String cmccNbIotSvrUrl = this.getCmccOneNetDomain();
		String cmccApiKey = this.getCmccOneNetApiKey(PROJ_CUST_CMCC_NB_APIKEY, projectId);    
		StringBuffer urlBuf = new StringBuffer(cmccNbIotSvrUrl);
		urlBuf.append("/nbiot/execute/offline"); 
		urlBuf.append("?imei=").append(cmd.getImei());
		urlBuf.append("&obj_id=").append(cmd.getObj_id());
		urlBuf.append("&obj_inst_id=").append(cmd.getObj_inst_id());
		urlBuf.append("&res_id=").append(cmd.getRes_id());
		int timeOut = cmd.getTimeout();
		timeOut = (timeOut < 5) ? 5 : timeOut;
		timeOut = (timeOut > 40) ? 40 : timeOut;
		urlBuf.append("&timeout=").append(timeOut);
		
		String valid_time = cmd.getValid_time();
		if(valid_time != null && !valid_time.trim().equals("")){
			urlBuf.append("&valid_time=").append(valid_time);
		}
		String expired_time = cmd.getExpired_time();
		if(expired_time == null || expired_time.trim().equals("")){
			SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
			// 默认有效期为24小时；
			expired_time = formater.format(new Date(System.currentTimeMillis() + 24*3600*1000L));
		}
		urlBuf.append("&expired_time=").append(expired_time);
		urlBuf.append("&retry=").append(cmd.getRetry());
		urlBuf.append("&trigger_msg=").append(cmd.getTrigger_msg());
		
		// masterApiKey, netnet api要求放到header中.
		Map<String, String> headerParaMap = new HashMap<String, String>();
		headerParaMap.put("api-key", cmccApiKey);

		// post-req-para
		JSONObject argsJson = new JSONObject();
		argsJson.put("args", cmd.getArgs());
		CmccNBCmdAck ack = new CmccNBCmdAck();
		logger.info("execCmccOneNetOfflineCmd cmd: {}, url={}, args={}", cmd.toString(), urlBuf.toString(), argsJson.toString());
		try{
			String rsp = HttpClientUtils.sendHttpPostJsonWithHeadPara(urlBuf.toString(), argsJson.toString(), headerParaMap, null);
			logger.info("execCmccOneNetOfflineCmd rsp: "+rsp);
			JSONObject ackJson = new JSONObject(rsp);
			String errNoStr = JsonUtils.getJsonAttrVal(ackJson, "errno", "0");
			String error = JsonUtils.getJsonAttrVal(ackJson, "error", "");
			ack.setErrno(Integer.parseInt(errNoStr));
			ack.setError(error);
		}catch(Exception e){
			logger.error("execCmccOneNetOfflineCmd failed: {}", cmd.toString(), e);
			// 非onenet的错误码.
			ack.setErrno(2); 
			ack.setError(e.getMessage());
		}
		return ack;
	}

	// 执行Cmcc-OneNet透传命令（针对GPRS-TCP透传设备，fntype=4097）.
	public CmccNBCmdAck execCmccOneNetTcpTranferCmd(CmccNBCmdReq cmd, DevBase dev) throws Exception {
		// URL: http://api.heclouds.com/cmds
		String cmccNbIotSvrUrl = this.getCmccOneNetDomain();
		String cmccApiKey = this.getCmccOneNetApiKey(PROJ_CUST_CMCC_GPRS_APIKEY, dev.getProjectId());    
		StringBuffer urlBuf = new StringBuffer(cmccNbIotSvrUrl);
		urlBuf.append("/cmds"); 
		urlBuf.append("?device_id=").append(dev.getDevEui());
		int timeOut = cmd.getTimeout();
		timeOut = (timeOut < 0) ? 0 : timeOut;
		timeOut = (timeOut > 2678400) ? 2678400 : timeOut;
		urlBuf.append("&timeout=").append(timeOut);
		
		// masterApiKey, netnet api要求放到header中.
		Map<String, String> headerParaMap = new HashMap<String, String>();
		headerParaMap.put("api-key", cmccApiKey);
		
		// post-req-para: <cmd> 
		CmccNBCmdAck ack = new CmccNBCmdAck();
		logger.info("execCmccOneNetTCPTransferCmd cmd: {}, url={}, args={}", cmd.toString(), urlBuf.toString(), cmd.getArgs());
		try{
			String rsp = HttpClientUtils.sendHttpPostJsonWithHeadPara(urlBuf.toString(), cmd.getArgs(), headerParaMap, null);
			logger.info("execCmccOneNetTCPTransferCmd rsp: "+rsp);
			JSONObject ackJson = new JSONObject(rsp);
			String errNoStr = JsonUtils.getJsonAttrVal(ackJson, "errno", "0");
			String error = JsonUtils.getJsonAttrVal(ackJson, "error", "");
			JSONObject retData = JsonUtils.getSubObject(ackJson, "data");
			ack.setErrno(Integer.parseInt(errNoStr));
			ack.setError(error);
			if(retData != null){
				ack.setData(retData.toString());
			}
		}catch(Exception e){
			logger.error("execCmccOneNetTCPTransferCmd failed: {}", cmd.toString(), e);
			// 非onenet的错误码.
			ack.setErrno(2); 
			ack.setError(e.getMessage());
		}
		return ack;
	}
	
	/**
	 * 判断是否为CMCC-tcp透传设备. fntype=4097
	 * @param baseDev
	 * @return
	 */
	public boolean isCmccTcpTransferDev(DevBase baseDev) {
		try{
			String devTypeId = baseDev.getDevType();
			DevTpExtData dtpExt = this.devtpMapper.getDevTpExtData(devTypeId);
			if(dtpExt == null){
				return false;
			}
			return NB_DEV_TCPT_TRANSFER_FNTYPE == dtpExt.getFntype();
		}catch(Exception e){
			logger.error("isCmccTcpTransferDev failed: "+e.getMessage());
		}
		return false;
	}

	/**
	 * 构造NBIOT-API的命令参数（request请求body参数字符串）；
	 * 如：cmcc-onenet-api的 args参数。
	 * 
	 * @return
	 * @throws Exception
	 */
	private String buildNbIotCmdArgs(ProjectDev dev, String payload, Map<String, Object> extParaMap) throws Exception {
		String devTypeId = dev.getDevtype();
		Invocable jsRunner = this.container.getJsPnpService().getJsPLugin(devTypeId);
		if(jsRunner == null){
			logger.error("[*!!*]Can not find the JS plugin for devtype="+devTypeId);
			throw new Exception("Can not find the JS plugin for devtype="+devTypeId);
		}		
		JSONObject json = new JSONObject(payload);
		String cmdCode = JsonUtils.getJsonAttrVal(json, "cmd", null);
		if(cmdCode == null){
			logger.error("[*@@*]Can not get cmd code from payload[cmd]: "+payload);
			throw new Exception("Can not get cmd code from payload[cmd]: "+payload);
		}
		Map<String, Object> cmdParaMap = null;
		JSONObject jsonParaObj = JsonUtils.getSubObject(json, "paras");
		if(jsonParaObj == null){
			cmdParaMap = new HashMap<String, Object>(0);
		} else {
			cmdParaMap = JsonUtils.jsonStrToHashMap(jsonParaObj.toString());
		}
		JSParam jsParam = new JSParam();
		jsParam.setDeveui(dev.getDeveui());
		jsParam.setDevtypeid(devTypeId);
		jsParam.setDevport("0");
		jsParam.setParas(cmdParaMap);
		if(extParaMap != null){
			jsParam.setDevExtDatas(extParaMap);
		}
		byte[] pbs = (byte[])jsRunner.invokeFunction(cmdCode, jsParam);	
		if(pbs == null || pbs.length == 0){
			throw new Exception("下发命令构造错误，请检查命令参数设置！");
		}
		String str = new String(pbs, "UTF-8");
		logger.info("Cmd args build ret: {}, origin-payload={}", str, payload);
		return str;
	}
	
	/**
	 * 从系统参数配置表查询onenet的域名配置.
	 * @return
	 */
	private String getCmccOneNetDomain() {
		try{
			SysParameter para = this.sysParaService.querySysParam(SYSPARA_CMCC_API_CATEGORY, SYSPARA_CMCC_API_SVRDOMAIN);
			if(para == null){
				return CMCC_ONENET_SVRURL;
			}
			return para.getParamValue().trim();
		}catch(Exception e){
			logger.error("get CmccOneNetDomain from sysparameter failed!", e);
		}
		return CMCC_ONENET_SVRURL;
	}
	
	/**
	 * 从系统参数配置表查询onenet的APIKEY配置.
	 * @return
	 */
	private String getCmccOneNetApiKey(String projCustAttrName, String projectId) {
		try{		
			ProjCustAttrBean attrBean = this.projAttrService.queryProjCustAttrBean(projectId, projCustAttrName);
			if(attrBean != null){
				String apiKey = attrBean.getAttrvalue();
				return (apiKey == null) ? "" : apiKey.trim();
			}
			SysParameter para = this.sysParaService.querySysParam(SYSPARA_CMCC_API_CATEGORY, SYSPARA_CMCC_API_APIKEY);
			if(para == null){
				return "";
			}
			return para.getParamValue().trim();
		}catch(Exception e){
			logger.error("get getCmccOneNetApiKey from sysparameter failed!", e);
		}
		return "";
	}
	
}
